package entity

import (
	"context"
	"errors"
	"fmt"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/redislib"
	"github.com/redis/go-redis/v9"
)

/*
对象在redis里的路由信息
就是存着对象在哪些服务器上面有实例化
*/

type EntitySrvInfo struct {
	SrvID   uint64
	SpaceID uint64
	Stype   global.ServerTypeEnum
}

// 对象的redis操作集合
type RedisUtilEntity struct {
	eid uint64
	//获取redis操作的方法
	getRedisF func() *redislib.RedisHandleModel
	//对象在redis中的主Key
	key string
}

// 对象的redis操作集合
func NewRedisUtilEntity(eid uint64) (result *RedisUtilEntity) {
	result = new(RedisUtilEntity)
	result.eid = eid
	result.getRedisF = global.GetRedisAccess(global.RedisName_Entity).GetConn
	result.key = fmt.Sprintf("entitysrvinfo:%d", result.eid)

	return
}

// 注册这个对象信息 一般是有链接的服务器会使用这个
func (util *RedisUtilEntity) RegEntity(etyp string, dbid uint64) (err error) {
	// 注册对象信息
	c := util.getRedisF()
	return c.HSet(c.Ctx, util.key, "entitytype", etyp, "dbid", dbid).Err()
}

// 注销对象信息 一般是有链接的服务器会使用这个
func (util *RedisUtilEntity) UnregEntity() error {
	c := util.getRedisF()
	return c.Del(c.Ctx, util.key).Err()
}

// 注册服务器
func (util *RedisUtilEntity) RegSrvID(stype global.ServerTypeEnum, srvid, spid uint64) error {
	c := util.getRedisF()
	return c.HSetNX(c.Ctx, util.key, fmt.Sprint(stype), util.getRouter(srvid, spid)).Err()
}

// UnRegSrvID 删除注册信息
func (util *RedisUtilEntity) UnRegSrvID(srvType global.ServerTypeEnum, srvID uint64, spaceID uint64) (result error) {
	red := util.getRedisF()
	result = red.Watch(red.Ctx, func(tx *redis.Tx) error {
		_srvID, _spaceID, err := util.getSrvInfo(srvType, tx)
		if err != nil && !errors.Is(err, redis.Nil) {
			return err
		}
		if _srvID == srvID && _spaceID == spaceID {
			_, err = tx.TxPipelined(red.Ctx, func(p redis.Pipeliner) error {
				return p.HDel(red.Ctx, util.key, fmt.Sprint(srvType)).Err()
			})
			if err != nil {
				logger.Error(err)
				return err
			}
		}
		return nil
	}, util.key)
	if result != nil {
		return result
	}
	return nil
}

// 获取对象在所有服务器上的路由信息
func (util *RedisUtilEntity) GetSrvInfos() (result []*EntitySrvInfo, err error) {
	c := util.getRedisF()
	mli, err := c.HgetallByStringMap(util.key)
	if err != nil {
		return nil, err
	}
	result = make([]*EntitySrvInfo, 0, len(mli))
	for k, v := range mli {
		styp, err := common.NewString(k).ToUint64()
		if err != nil {
			continue
		}
		srvid, spid, err := util.scanRouter(v)
		if err != nil {
			logger.Warnf("invalid srv router format: %s", v)
			continue // 跳过非法数据
		}
		md := &EntitySrvInfo{
			SrvID:   srvid,
			SpaceID: spid,
			Stype:   global.ServerTypeEnum(styp),
		}
		result = append(result, md)
	}
	return result, nil
}

// 获取指定服务类型信息
func (util *RedisUtilEntity) GetSrvInfoByStype(styp global.ServerTypeEnum) (srvid, spid uint64, err error) {
	c := util.getRedisF()
	if v, err := c.HGet(context.Background(), util.key, fmt.Sprint(styp)).Result(); err != nil {
		return 0, 0, err
	} else {
		srvid, spid, err = util.scanRouter(v)
		return srvid, spid, err // 直接返回错误
	}
}

// 包活会话
func (this *RedisUtilEntity) Expire() {
	c := this.getRedisF()
	c.Expire(c.Ctx, this.key, global.Expire1Day)
}

/////////////////////////////////////////////////////////////
/* 私有函数 */

// 获取指定服务类型信息
func (util *RedisUtilEntity) getSrvInfo(styp global.ServerTypeEnum, c redis.Cmdable) (srvid, spid uint64, err error) {
	if v, err := c.HGet(context.Background(), util.key, fmt.Sprint(styp)).Result(); err != nil {
		return 0, 0, err
	} else {
		srvid, spid, err = util.scanRouter(v)
		return srvid, spid, err // 直接返回错误
	}
}

func (util *RedisUtilEntity) getRouter(srvid, spid uint64) string {
	return fmt.Sprintf("%d:%d", srvid, spid)
}

func (util *RedisUtilEntity) scanRouter(v string) (srvid, spid uint64, err error) {
	n, err := fmt.Sscanf(v, "%d:%d", &srvid, &spid)
	if err != nil || n != 2 {
		return 0, 0, fmt.Errorf("invalid router format: %s", v)
	}
	return srvid, spid, nil
}
